home *** CD-ROM | disk | FTP | other *** search
/ ASME's Mechanical Engine…ing Toolkit 1997 December / ASME's Mechanical Engineering Toolkit 1997 December.iso / ai / prlg195b.lzh / CHART.LZH / CHART.TXT < prev    next >
Text File  |  1987-03-31  |  12KB  |  217 lines

  1. .pn1
  2. .po4
  3. A VERY SIMPLE CHART PARSER
  4.  
  5. Peter Ross
  6. Dept. of AI, University of Edinburgh,
  7. 80 South Bridge,
  8. Edinburgh EH1 1HN,
  9. Scotland.
  10.  
  11. This describes how to use the (very) simple chart parser written in Prolog
  12. in the file "chart.pro". You will need LOTS of stack space - the program
  13. does no assertion or retraction at run time, but keeps all the run-time
  14. data in lists.
  15.  
  16. The program expects data in the form of Prolog clauses:
  17.  
  18. [1] rule(Tag, Category, ExpansionList).
  19.         The 'Tag' argument is just an arbitrary Prolog term used to group
  20.         together a set of rule/3 and lexical/3 clauses. The parser needs
  21.         to be told this tag in order to know which set of these clauses
  22.         to use as data. Since it is arbitrary, you could use a compound
  23.         term to allow you to selectively include rules, at no extra cost.
  24.         The 'Category' argument identifies a syntactic category, e.g.
  25.         noun or sentence.
  26.         The 'ExpansionList' is a list giving one possible expansion of that
  27.         category into sub-categories.
  28.         Example:
  29.                 rule(simple, sentence, [noun_phrase, verb_phrase]).
  30.         The categories (both Category and those in ExpansionList) can be
  31.         compound terms, thus allowing the use of variables for result passing
  32.         and context setting. The parser copies terms as needed, when
  33.         unification might be a bad thing to do because it might cause
  34.         supposedly independent data structures to become too specific.
  35.         This copying, by the way, is done by dismantling the source structure
  36.         and building a duplicate rather than by the crude assert/retract
  37.         method; the latter tends to be much slower but takes less run-time
  38.         space.
  39.  
  40.         NOTE: categories are not supposed to be defined recursively.
  41.         Nevertheless, the code does check whether you are adding an edge you
  42.         already have. The check does slow things a bit, but is specifically
  43.         a check against left recursion at the time when empty active edges
  44.         are added. A fuller check against adding duplicate inactive edges
  45.         has been commented out; you probably don't need it unless you mess
  46.         with the rule tags at run time so as to cause new parsing rules to
  47.         be introduced during parsing.
  48.  
  49. [2] lexical(Tag, Word, CategoryList).
  50.         The 'Tag' argument is as above.
  51.         The 'Word' is a word that may legitimately appear in the input.
  52.         The 'CategoryList' is a list of the possible syntactic categories
  53.         which that word might fit, e.g. 'fly' might be a noun or verb.
  54.         As above, the categories can be arbitrary terms.
  55.  
  56. [3] initial_category(Tag, Category).
  57.         This states what the highest level category is - it should be unique.è
  58. [4] strategy(Tag, Strategy).
  59.         This should instantiate Strategy to either 'td' for top-down or
  60.         'bu' for bottom-up. The parser does not check it for validity.
  61.  
  62. [5] policy(Tag, Policy).
  63.         This should instantiate Policy to either 'df' for depth-first or
  64.         'bf' for breadth-first. The parser does not check it for validity.
  65.  
  66. [6] ersatz_category(Tag, DummyCategoryName).
  67.         By default, the category name 'user' is specially used by the system.
  68.         It supposes that there is an extra rule of the form
  69.                 rule(Tag, user, [TopLevelCategory])
  70.         when doing top-down parsing. This extra category will of course appear
  71.         in the final chart, and should help in the later analysis of it. If
  72.         you are really attached to the category name 'user' but want to do
  73.         some top-down parsing, then you can persuade the system to use a
  74.         different name instead by including a clause of this form.
  75.         Although this ersatz category can be useful, its original purpose
  76.         was to make the program shorter at negligible speed cost by ensuring
  77.         that the top-level category appeared on the left of a single rule.
  78.  
  79. IMPORTANT: The rule/3 and lexical/3 predicates are only used during the
  80.            initialisation stage, when the transformed rules are constructed
  81.            and when the initial chart is created.
  82.  
  83. There are three important data structures manipulated by the program:
  84.  
  85. [a] edge(Category, FoundList, NeedsList, StartVertex, EndVertex).
  86.         This describes an edge in the chart. If you don't know what this
  87.         means, read Winograd's book "Language As A Cognitive Process, vol 1"
  88.         or the articles by Henry Thompson and Graeme Ritchie in "Artificial
  89.         Intelligence: Tools, Techniques and Applications" by O'Shea and
  90.         Eisenstadt (published by Harper and Row).
  91.         There are three odd details. Items on the 'FoundList' are of the form
  92.                 Category = VertexNumber
  93.         (the starting, left-hand vertex of that instance of the category).
  94.         A word from the input will appear in the 'FoundList' as
  95.                 word(Word) = VertexNumber
  96.         The 'FoundList' is ordered so that the most recently found category is
  97.         first.
  98.  
  99. [b] the chart: ActiveEdgeList + InactiveEdgeList.
  100.         The edges are segregated into two lists for convenience.
  101.         The parser returns such a chart when it finishes.
  102.  
  103. [c] the agenda: CandidateList - Hole.
  104.         This is a difference list (a list with a variable at the end - 'Hole'
  105.         is the variable). The items in the list are of the form
  106.                 ActiveEdge + InactiveEdge
  107.         and are candidates (guaranteed successful, in fact) for an application
  108.         of the fundamental rule of chart parsing.
  109.         The default monitoring system prints out agendas.
  110.  
  111. The program has two top level predicates. They are:è
  112. [i]   parse(Tag, WordList, MaxVertex, Chart).
  113.         The Tag and WordList must be given. The MaxVertex and Chart must be
  114.         variables. When the parse is done, MaxVertex will be instantiated to
  115.         the largest vertex number (the lowest is 0), and Chart will be
  116.         instantiated to the final chart. When doing top-down parsing, the
  117.         parser adds one ersatz rule of the form
  118.                 rule(Tag,user,[TopMostCategory])
  119.         - there will be edges for the ersatz category 'user' to help you to
  120.         examine the chart afterwards for successful parsings. You can
  121.         substitute what you want for 'user' - see above. This predicate does
  122.         some transformations on the rule/3 clauses supplied by the user. The
  123.         transformations are all done by the predicate invert_rules(Tag).
  124.  
  125. [ii]  make_chart(Tag, WordList, MaxVertex, Chart).
  126.         This is exactly like parse/4 above, but assumes that the rule
  127.         transformations have already been done.
  128.  
  129. When a parse has been done and a final chart has been produced, you may
  130. want to add to the word list and extend the chart, for example if using the
  131. parser for plan recognition. You can do so by the following means: call
  132.         initial_setup(Tag, Strategy, Policy, NewWordList,
  133.                       MinVertex, NewMaxVertex,
  134.                       OldChart, NewInitialChart,
  135.                       AgendaOfYourChoice, NewAgenda),
  136.         chart(Tag, Strategy, Policy, NewInitialChart, NewAgenda, FinalChart)
  137. This will assume that more words are added between MinVertex and NewMaxVertex,
  138. and will add to the OldChart and AgendaOfYourChoice you gave it, to create
  139. a new FinalChart. Since all the information needed about the presence of
  140. any previous set of words (presumably ending at MinVertex) will be contained
  141. in the OldChart you got out of parsing that previous word sequence, this will
  142. incrementally add to the chart.
  143.  
  144. Normally parsing stops when the parser runs out of agenda. You may discover
  145. a need to suspend parsing under program control, for example if you want
  146. to doctor the chart or extract useful information from it before the entire
  147. input sequence is available. If so, you may redefine the test
  148.         stop_parser(Tag,Strategy,Policy,Chart,Agenda,FinalChart)
  149. which currently does no more than instantiate the result FinalChart to
  150. Chart when Agenda is empty. It is the success of this predicate which halts
  151. parsing.
  152.  
  153. The implementation of the fundamental rule is explicit within the code, so
  154. that it is easy to change for your own purposes.
  155.  
  156.  
  157. There is a simple monitoring mechanism. The predicates
  158.         watch(Tag)      nowatch(Tag)
  159. turn it on or off for the specified rule set. If on, it reports when the
  160. rule transformations are complete, and whenever an item on the agenda is
  161. processed, it tries to call the predicate
  162.         user_mon(Tag,Strategy,Policy,OldChart,OldAgenda,NewChart,NewAgenda)
  163. If this fails, it uses a default strategy of printing out the new chart and
  164. agenda and waiting for a <cr> before continuing.
  165. èThere is a simple test rig, called by the predicate
  166.         test(Tag).
  167. It makes use of a trivial utility
  168.         print_chart(Chart)
  169. to print out the active and inactive edges after the parse has finished.
  170. The lists of edges are sorted before being printed, into the standard order
  171. of Prolog terms.
  172.  
  173. A sample rule set can be found in the files "test01.pro" and "test02.pro".
  174.  
  175.  
  176. IMPLEMENTATION: SOME THOUGHTS
  177.  
  178. This program clearly shows the need for some kind of user-definable indexing
  179. in Prolog in order to make such things efficient. Unfortunately record/3 is
  180. not much use, because the key can only usefully be atomic (only the principal
  181. functor name matters) and in C-Prolog the key cannot be an integer (nor is
  182. record/3 that fast in C-Prolog). At present all you've really got is the
  183. clause indexing mechanism, so you're stuck with assert/retract and all that
  184. that implies if you want to cut down on searching through big data structures
  185. such as long lists. Gimme arrays, gimme hunks, gimme a decent record package,
  186. gimme hash tables, gimme something!! Prolog-2 does let you specify hash tables
  187. for particular predicates, but these are only used by the calling mechanism.
  188. Wouldn't it be nice to have some way of specifying that anything with a given
  189. principal functor was to be accessed through a set of hash tables,
  190. sub-indexing specifiable by the user in some way, e.g. akin to that offered
  191. by PEARL for frame access? Some other Prologs (e.g. Arity Prolog) do now
  192. offer such things as B-trees and hash tables for term storage.
  193.  
  194. At the moment, for simplicity, the chart is only represented as two lists -
  195. one of active edges, one of inactive edges. Since the chart only grows, never
  196. shrinks, it might be better to represent it as two ordered trees peppered with
  197. holes (variables). This ought to speed things up a lot.
  198.  
  199. I considered the idea of not allowing Prolog variables in the terms which
  200. represent categories; this would end all temptation to misuse unification.
  201. Instead, I have in the past used terms of the form ?Atom as variables (where
  202. ?/1 is defined as a prefix operator, none of this messy atom exploding done
  203. by LISPers who don't/can't alter their character tables). This leads to
  204. passing binding lists about a lot. As far as the ersatz unification in edge
  205. generation goes, there is no significant speed difference between using
  206. Prolog variables and these pseudo-variables. However, there is a difference
  207. in speed when looking for candidate edge pairs, since with Prolog variables
  208. I can just use the dodge of specifying \+(\+(Category1=Category2)), which
  209. checks whether they will unify, without doing so, in a reasonably efficient
  210. way. With pseudo-variables I'd need to indulge in term smashing or suffer
  211. the overhead of carrying binding lists about in the chart. The latter is not
  212. so awful as it might look, since it would be possible to construct the unifier
  213. at the same time as checking candidacy, and just carry the unifier about till
  214. needed. But, this still looks a bit worse than using Prolog variables.
  215.  
  216.  
  217.